package com.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

@Configuration
@EnableAuthorizationServer //开启认证服务
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    /**
     *  AuthorizationServerSecurityConfigurer继承SecurityConfigurerAdapter.
     *  也就是一个 Spring Security安全配置提供给AuthorizationServer
     *  去配置AuthorizationServer的端点（/oauth/****）的安全访问规则、过滤器Filter。

     * 认证服务器的安全访问配置
     * 认证服务最终是以api接口的方式对外服务（校验合法性并生成令牌，令牌校验）
     * 以api接口对外的话，就涉及到接口的访问权限。这里进行必要的配置
     * 也就是认证服务器的 访问权限
     *  配置：安全检查流程,用来配置令牌端点（Token Endpoint）的安全与权限访问
     *  默认过滤器：BasicAuthenticationFilter
     *  1、oauth_client_details表中clientSecret字段加密【ClientDetails属性secret】
     *  2、CheckEndpoint类的接口 oauth/check_token 无需经过过滤器过滤，默认值：denyAll()
     * 对以下的几个端点进行权限配置：
     * /oauth/authorize：授权端点
     * /oauth/token：令牌端点
     * /oauth/confirm_access：用户确认授权提交端点
     * /oauth/error：授权服务错误信息端点
     * /oauth/check_token：用于资源服务访问的令牌解析端点
     * /oauth/token_key：提供公有密匙的端点，如果使用JWT令牌的话
     **/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
//        相当于打开访问接口的 开关。能够访问接口
        security.allowFormAuthenticationForClients() ;//允许客户端表单认证
        security.tokenKeyAccess("permitAll()");//开启端口的访问权限 /oauth/token_key 的访问权限
        security.checkTokenAccess("permitAll()");//验证令牌有效性合法性  /oauth/check_token 访问 权限

    }

    /***
     * 客户端详情配置 例如 client_id secret,l
     * 例如 拉钩网 用qq登录
     * 拉钩就是客户端
     * 资源拥有者就是我们自己
     * 资源服务就是qq
     * 资源认证服务也是qq
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        clients.inMemory()
//                .withClient("zgc") //clientId
//                .secret("abc123") //密码
//                .resourceIds("user")//客户端要访问的资源服务的ID清单 ，ID需求在资源服务进行配置
//                .authorizedGrantTypes("password","refresh_token") //认证类型 ，客户端传递参数进行指定
//                .scopes("all") //权限范围
        ;
//        从数据库加载客户端详情
        clients.withClientDetails( jdbcClientDetailsService());
    }

    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService(){
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        return jdbcClientDetailsService;
    }

    /**
     * token令牌管理相关 当下的token 生成，需要存储，
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokeStore()) //token 的存储
                .tokenServices(tokenService()) //token的生成细节，有效时间等
                .authenticationManager(authenticationManager) //认证管理器 负责用户名密码的认证
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST) //token的校验方式

        ;
    }
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true); //支持令牌刷新
        defaultTokenServices.setTokenStore(tokeStore());
//        设置令牌有效时间
        defaultTokenServices.setAccessTokenValiditySeconds(20);  //20秒
//        刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200);//3天
//        配置令牌增强
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        return defaultTokenServices;
    }

    /**
     * 定义令牌存储
     * @return
     */
    @Bean
    public TokenStore tokeStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Autowired
    private MyAccessTokeConvoter myAccessTokeConvoter;

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("zgc");
        jwtAccessTokenConverter.setVerifier(new MacSigner("zgc")); //验证jwt 使用的密钥
        jwtAccessTokenConverter.setAccessTokenConverter(myAccessTokeConvoter);
        return jwtAccessTokenConverter;
    }






}
